# -*- coding: utf-8 -*-
import time
import threading
import redis
from typing import Callable, Dict, List

from functools import partial

from contract_market.core.core import EventCenter
from contract_market.base.engine import EventEngineMeta
from contract_market.base.event import Event
from contract_market.initialize.redis import OPRedis
from contract_market.utils.dqueue import PriorityQueue


class EventEngine:
    """ """

    def __init__(self, event_center: EventCenter) -> None:
        """ """
        self._engines: Dict[str, EventEngineMeta] = dict()
        self.event_center: EventCenter = event_center
        self.init()
        self.init_add_event()

    def init(self):
        """ """
        self.event_center.start()

    def init_add_event(self):
        """ """
        self.listener = QuantListener(self.event_center, self)
        pass

    def add_extra_func(self, name, func: Callable):
        """ """
        setattr(self, name, func)

    def add_extra_class(self, name, obj: object):
        """ """
        setattr(self, name, obj)

    def init_engine(self):
        """ """
        pass

    def add_engine(self, engine_cls: EventEngineMeta, **kw):
        """ """
        if kw:
            engine = engine_cls(self.event_center, self, **kw)
        else:
            engine = engine_cls(self.event_center, self)
        self._engines.setdefault(engine.__class__.__name__, engine)
        return engine

    def engines(self):
        """ """
        return self._engines

    def remove_engine(self, engine_cls: EventEngineMeta):
        """ """
        engine = engine_cls(self.event_center, self)
        name = engine.__class__.__name__
        if name in self._engines:
            self._engines[name].stop()
            return self._engines.pop(name)
        return

    engines: Dict[str, EventEngineMeta] = property(engines)

    def stop(self):
        """ """
        for _, engine in self.engines.items():
            engine.stop()

    def on_event(self, event: Event):
        self.event_center.register(event.event_type, event.event_data)

    def send(self, event):
        self.event_center.send(event)

    def start(self):
        """ engine-start """
        for engine in self._engines.values():
            engine.start()


class QuantListener(EventEngineMeta):
    """ 
    Listener quotation
        * on_ticker - ticker
        * on_depth - *
        * on_trade - *
        * on_user_trade - user.order
    """

    TOPIC_TICKER = "ticker"

    TOPIC_USER_BALANCE = "user.balance"
    TOPIC_USER_POSITION = "user.position"
    TOPIC_USER_ORDER = "user.order"
    TOPIC_USER_TITLE = "sub_user"
    TOPIC_FORMAT_USER_UM = "xt_contract_user-um:{}:{}:{}:push"
    TOPIC_FORMAT_UM = "xt_contract-um:{}:{}:push"
    TOPIC_INDEX_PRICE = 'markPrice'

    def __init__(self, event_center: EventCenter, event_engine: EventEngine) -> None:
        """ 
        ::param topic : accesskey
        """
        super().__init__(event_center, event_engine)
        self._client: redis.Redis = OPRedis()
        self._lock = threading.Lock()
        self._active = False
        self._t = []
        self._add_extra_func()

    def _add_extra_func(self):
        """ """
        self.event_engine.add_extra_func(
            self.on_ticker.__name__, self.on_ticker)
        self.event_engine.add_extra_func(self.on_depth.__name__, self.on_depth)
        self.event_engine.add_extra_func(self.on_trade.__name__, self.on_trade)
        self.event_engine.add_extra_func(
            self.on_user_trade.__name__, self.on_user_trade)
        self.event_engine.add_extra_func(
            self.on_user_balance.__name__, self.on_user_balance)
        self.event_engine.add_extra_func(
            self.on_user_position.__name__, self.on_user_position)
        self.event_engine.add_extra_func(
            self.on_index_price.__name__, self.on_index_price)

        self.event_engine.client = self._client

    def on_topic(self, queue: PriorityQueue, topic):
        """ """
        
        p = self._client.pubsub()
        p.subscribe(topic)
        # Listen
        while True:
            try:
                msg = p.get_message()
                if msg and msg["type"] == "message":
                    queue.put(msg)
            except Exception as e:
                print("[e] ", e)
                pass
            time.sleep(0.01)


    def dispatch(self, queue: PriorityQueue, topic: str) -> PriorityQueue:
        """ """
        self.on_topic(queue, topic)

    def _thread(self):
        """ """
        func = partial(threading.Thread, target=self.dispatch, daemon=True)
        return func

    def add_task(self, func):
        """ """
        with self._lock:
            self._t.append(func)

    def on_event(self, topic, maxlen=100):
        """ """
        queue = PriorityQueue(maxlen=maxlen)
        func = self._thread()(args=(queue, topic))
        func.start()
        self.add_task(func)
        return queue

    # xt_contract-um:1inch_usdt:ticker
    def on_ticker(self, topic) -> PriorityQueue:
        """ """
        topic = self.TOPIC_FORMAT_UM.format(topic, self.TOPIC_TICKER)
        print(topic)
        return self.on_event(topic)

    def on_depth(self, topic) -> PriorityQueue:
        """ """
        # TODO
        return self.on_event(topic)

    def on_trade(self, topic) -> PriorityQueue:
        """ """
        # TODO
        return self.on_event(topic)

    def on_user_trade(self, topic) -> PriorityQueue:
        """ """
        topic = self.TOPIC_FORMAT_USER_UM.format(
            self.TOPIC_USER_TITLE, topic, self.TOPIC_USER_ORDER)
        return self.on_event(topic)

    def on_user_balance(self, topic) -> PriorityQueue:
        """ """
        topic = self.TOPIC_FORMAT_USER_UM.format(
            self.TOPIC_USER_TITLE, topic, self.TOPIC_USER_BALANCE)
        return self.on_event(topic, maxlen=1)

    def on_index_price(self, topic) -> PriorityQueue:
        """ """
        topic = self.TOPIC_FORMAT_UM.format(
            topic, self.TOPIC_INDEX_PRICE)
        return self.on_event(topic, maxlen=1)

    def on_user_position(self, topic) -> PriorityQueue:
        """ """
        topic = self.TOPIC_FORMAT_USER_UM.format(
            self.TOPIC_USER_TITLE, topic, self.TOPIC_USER_POSITION)
        return self.on_event(topic)

    def stop(self):
        """ """
        self._active = False
        for t in self._t:
            t.join()